package com.ajavaer.framework.common.tools;


import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * json处理工具
 *
 * @author 李永曜
 * @time 2014-7-9下午11:39:13
 * <p>
 * jackson 的注解:
 * <p>
 * •@JsonIgnoreProperties 此注解是类注解，作用是json序列化时将java
 * bean中的一些属性忽略掉，序列化和反序列化都受影响。<br/>
 * •@JsonIgnore此注解用于属性或者方法上（最好是get方法上），作用和上面的@JsonIgnoreProperties一样。<br/>
 * •@JsonFormat此注解用于属性或者方法上（最好是get方法上），可以方便的把Date类型直接转化为我们想要的模式，比如@JsonFormat
 * (pattern = "yyyy-MM-dd HH-mm-ss")<br/>
 * •@JsonSerialize此注解用于属性或者getter方法上，用于在序列化时嵌入我们自定义的代码，
 * 比如序列化一个double时在其后面限制两位小数点。<br/>
 * </p>
 * @see <a href="http://blog.csdn.net/nomousewch/article/details/8955796">blog</a>
 */
@SuppressWarnings("deprecation")
public class JsonTools {

    private static Logger log = LoggerFactory.getLogger(JsonTools.class);
    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, false);
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        MAPPER.registerModule(timeModule).registerModule(new Jdk8Module());
    }

    private JsonTools() {
    }

    private static final JsonFactory JSONFACTORY = new JsonFactory();

    public static String beanToJson(Object o) {
        StringWriter sw = new StringWriter(300);
        try (JsonGenerator jsonGenerator = JSONFACTORY.createGenerator(sw)) {
            MAPPER.writeValue(jsonGenerator, o);
            return sw.toString();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T jsonToBean(String json, Class<?> clazz) {
        try {
            return (T) MAPPER.readValue(json, clazz);
        } catch (Exception e) {
            log.error("jsonToBean failed,json:" + json + " class:" + clazz, e);
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> beanToMap(Object o) {
        try {
            return MAPPER.readValue(beanToJson(o), HashMap.class);
        } catch (Exception e) {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> jsonToMap(String json, boolean collToString) {
        try {
            Map<String, Object> map = MAPPER.readValue(json, HashMap.class);
            if (collToString) {
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    if (entry.getValue() instanceof Collection
                            || entry.getValue() instanceof Map) {
                        entry.setValue(beanToJson(entry.getValue()));
                    }
                }
            }
            return map;
        } catch (Exception e) {
            log.error("jsonToMap failed,json:" + json, e);
            return null;
        }
    }

    public static String listToJson(List<Map<String, String>> list) {
        StringWriter sw = new StringWriter();
        try (JsonGenerator jsonGenerator = JSONFACTORY.createGenerator(sw)) {
            new ObjectMapper().writeValue(jsonGenerator, list);
            jsonGenerator.flush();
            return sw.toString();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static List<Map<String, String>> jsonToMapList(String json) {
        if (json != null && !"".equals(json.trim())) {
            try (JsonParser jsonParse = JSONFACTORY.createParser(new StringReader(json))) {
                return (ArrayList<Map<String, String>>) new ObjectMapper().readValue(jsonParse, ArrayList.class);
            } catch (Exception e) {
                log.error("jsonToMapList failed,json:" + json, e);
            }
        }
        return null;
    }

    /**
     * 获取泛型的Collection Type
     *
     * @param collectionClass 泛型的Collection
     * @param elementClasses  元素类
     * @return JavaType Java类型
     */
    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> jsonToList(String json, Class<T> clazz) {
        JavaType javaType = getCollectionType(ArrayList.class, clazz);
        List<T> list = null;
        try {
            list = MAPPER.readValue(json, javaType);
        } catch (IOException e) {
            log.error("jsonToList failed,json:" + json, e);
        }
        return list;
    }

    public static <R, T> R jsonToBean(String json, Class<R> type, Class<T> parametricType) {
        JavaType javaType = getCollectionType(type, parametricType);
        R result = null;
        try {
            result = (R) MAPPER.readValue(json, javaType);
        } catch (IOException e) {
            log.error("jsonToBean failed,json:" + json, e);
        }
        return result;
    }

}